package bgai.gamelogic;

import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;

/**
 * Movimento di un checker, visto come sequenza ordinata di piu' movimenti singoli.
 * <p>
 * Questa classe si assicura solo che i singoli movimenti siano adiacenti fra loro.
 * @see Move
 * @see SingleMove
 */
public class CheckerMove implements Iterable<SingleMove>, Cloneable {
	/**
	 * Una MultipleMove altro non e' che una sequenza ordinata di SingleMove adiacenti
	 */
	private List<SingleMove> moves;
	
	/**
	 * Colore della pedina
	 */
	private CheckerColor color;
	
	/**
	 * Crea una mossa multiple vuota
	 */
	public CheckerMove(CheckerColor color) {
		moves= new Vector<SingleMove>();
		this.color= color;
	}
	
	/**
	 * Colore della pedina
	 * @return
	 */
	public CheckerColor getColor() {
		return color;
	}
	
	/**
	 * Aggiunge una mossa singola a questa mossa multipla. La mossa singola
	 * deve essere adiacente alle attuali mosse
	 * @param move
	 * @throws BadMoveException {@code move} non e' adiacente
	 */
	public void add(SingleMove move) throws BadMoveException {
		
		if (moves.isEmpty()) {
			moves.add(move);
		} else {
			if (!color.equals(move.getColor())) {
				throw new BadMoveException("Stai aggiungendo ad un CheckerMove un SingleMove di colore sbagliato");
			}
			
			// calcolo la posizione finale fin qui raggiunta
			int finalPos= getEndingPosition();
			
			// verifico che i SingleMove siano adiacenti
			if (finalPos!=move.getStartingPosition()) {
				throw new BadMoveException(this, "Mossa "+move+" non adiacente");
			}
			
			// tutto ok, aggiungo
			moves.add(move);
		}
	}
	
	/**
	 * Restituisce true se non sono presenti mosse
	 * @return
	 */
	public boolean isEmpty() {
		return moves.isEmpty();
	}
	
	/**
	 * Ottiene la posizione di partenza di questa mossa
	 * @return
	 * @throws PositionException non sono presenti mosse
	 */
	public int getStartingPosition() throws PositionException {
		if (moves.isEmpty()) {
			throw new PositionException(0, "Non sono presenti mosse");
		} else {
			return moves.get(0).getStartingPosition();
		}
	}
	
	/**
	 * Ottiene la posizione finale di questa mossa
	 * @return
	 * @throws PositionException non sono presenti mosse
	 */
	public int getEndingPosition() throws PositionException {
		if (moves.isEmpty()) {
			throw new PositionException(0, "Non sono presenti mosse");
		} else {
			return moves.get(moves.size()-1).getEndingPosition();
		}
	}
	
	/**
	 * Restituisce la lista non ordinata dei dadi usati per questa mossa
	 * @return
	 */
	public List<Integer> getUsedDices() {
		List<Integer> list= new LinkedList<Integer>();
		for (SingleMove m : moves) {
			list.add(m.getDice());
		}
		return list;
	}
	
	@Override
	public String toString() {
		
		if (isEmpty()) {
			return "<empty CheckerMove>";
		}
		
		String str;
		SingleMove sm;
		
		// inizio
		sm= moves.get(0);
		if (sm.fromBar()) str= "bar";
		else str= Integer.toString(sm.getStartingPosition());
		
		
		for (int k=0; k<moves.size(); k++) {
			sm= moves.get(k);

			str+= "/";			
			
			// fine
			if (sm.bearOff()) str+= "off";
			else str+= Integer.toString(sm.getEndingPosition());

			// hit?
			if (sm.isHit()) str+= "*";

		}

		return str;
	}

	public String rawToString() {
		String str= "[";
		boolean first= true;
		for (SingleMove sm : moves) {
			if (first) {
				str+= sm.rawToString();
				first= false;
			} else {
				str+= ", " + sm.rawToString();
			}
		}
		return str+"]";
	}
	
	@Override
    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof CheckerMove) {
        	CheckerMove cm = (CheckerMove) anObject;
        	
        	return this.moves.equals(cm.moves);
        }
        return false;
    }
	
	/**
	 * Iteratore sulle singole mosse
	 */
	@Override
	public Iterator<SingleMove> iterator() {
		return moves.iterator();
	}
	
	@Override
	public Object clone() {
		try {

			CheckerMove clone = (CheckerMove) super.clone();
			
			// l'unica cosa da fare e' clonare la lista
			clone.moves= new Vector<SingleMove>();
			for (int k=0; k<moves.size(); k++) {
				clone.moves.add( (SingleMove) moves.get(k).clone());
			}
			
			return clone;
			
		} catch (CloneNotSupportedException e) {
			// non accadra' mai
			throw new InternalError("Come puo' essere CloneNotSupportedException?");
		}
	}
	
	@Override
	public int hashCode() {
		return moves.hashCode();
	}
	
}
